/** * 该代码用于描述产品的结构信息 */
<template>
  <div style="position: relative; height: calc(70vh)">
    <div class="header">
      <button class="back-button" @click="back">返回</button>
      <span class="title">产品结构建模 / {{ rootname }}</span>
      <div class="header-buttons">
        <el-button type="primary" link :icon="Document" @click="saveModel">保存</el-button>
      </div>
    </div>
    <!-- 主内容区域，分为左侧的侧边栏和右侧的主工作区 -->
    <div class="content" style="display: flex; height: 100%; overflow: hidden">
      <!-- 左侧侧边栏 -->
      <aside class="sidebar">
        <!-- 添加侧边栏顶部的切换按钮 -->
        <div class="sidebar-tabs">
          <button :class="['sidebar-tab', { active: sidebarView === 'add' }]" @click="sidebarView = 'add'">快速添加材料</button>
          <button :class="['sidebar-tab', { active: sidebarView === 'current' }]" @click="sidebarView = 'current'">
            展示当前材料
          </button>
        </div>

        <!-- 快速添加材料视图 -->
        <div v-if="sidebarView === 'add'" class="sidebar-content">
          <div
            v-for="(material, index) in sidebarMaterials"
            :key="index"
            class="memo-item"
            @click="handleMaterialClick(material)"
          >
            <!-- 物料名称在顶部 -->
            <div class="memo-name">{{ material.materialName }}</div>
            <!-- 下方分为左右两部分 -->
            <div class="memo-content">
              <div class="memo-status">类型：{{ material.sourceType === 1 ? "自制" : "外购" }}</div>
              <button class="memo-button" @click.stop="addMaterial(material)">添加</button>
            </div>
          </div>
        </div>

        <!-- 当前材料视图 -->
        <div v-else class="sidebar-content">
          <div
            v-for="(material, index) in currentMaterials"
            :key="index"
            :class="['memo-item', { 'memo-item-selected': selectedMaterialId === material.id }]"
            @click="highlightMaterialNodes(material)"
          >
            <div class="memo-name">{{ material.materialName }}</div>
            <div class="memo-content">
              <div class="memo-status">类型：{{ material.sourceType === 1 ? "自制" : "外购" }}</div>
            </div>
          </div>
        </div>
      </aside>

      <!-- 右侧主工作区 -->
      <div class="flow-container">
        <Structure_Input ref="InputWindowRef" @node_add="handleNodeAdd"></Structure_Input>
        <Structure_Information ref="InformationWindowRef"></Structure_Information>
        <!-- 删除窗口 -->
        <div v-if="isModalDelOpen" class="modal">
          <div class="modal-content">
            <h4>确定要删除吗</h4>
            <button @click="deleteNode">确定</button>
            <button @click="closeDelModal">取消</button>
          </div>
        </div>

        <VueFlow
          v-model:nodes="nodes"
          v-model:edges="edges"
          fit-view-on-init
          class="confirm-flow"
          :default-zoom="1.5"
          :min-zoom="0.2"
          :max-zoom="4"
          @node-click="onNodeClick"
          @connect-start="onConnect1"
          @connect="onConnect2"
          @pane-click="clearSelection"
        >
          <Background color="white" />
          <!-- 节点模板 -->
          <template #node-productNode_homeMade="props">
            <productNode_homeMade :id="props.id" :data="props.data" @toolbar-action="handleToolbarAction" />
          </template>
          <template #node-productNode_outBuy="props">
            <productNode_outBuy :id="props.id" :data="props.data" @toolbar-action="handleToolbarAction" />
          </template>
          <template #node-productNode_outBuy1="props">
            <productNode_outBuy1 :id="props.id" :data="props.data" @toolbar-action="handleToolbarAction" />
          </template>
          <template #edge-button="buttonEdgeProps">
            <EdgeWithButton
              :id="buttonEdgeProps.id"
              :source-x="buttonEdgeProps.sourceX"
              :source-y="buttonEdgeProps.sourceY"
              :target-x="buttonEdgeProps.targetX"
              :target-y="buttonEdgeProps.targetY"
              :source-position="buttonEdgeProps.sourcePosition"
              :target-position="buttonEdgeProps.targetPosition"
              :marker-end="buttonEdgeProps.markerEnd"
              :style="buttonEdgeProps.style"
            />
          </template>
        </VueFlow>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { VueFlow, type Node, type Edge, MarkerType } from "@vue-flow/core";
import { Background } from "@vue-flow/background";
import { ProductTree } from "@/api/interface";
import { computed } from "vue";
import { useRoute } from "vue-router";
import { useTabsStore } from "@/stores/modules/tabs";
import productNode_homeMade from "@/views/proTable/components/productNode_homeMade.vue";
import productNode_outBuy from "@/views/proTable/components/productNode_outBuy.vue";
import productNode_outBuy1 from "@/views/proTable/components/productNode_outbuy1.vue";
import Structure_Input from "./components/structure_Input.vue";
import Structure_Information from "./components/structure_Information.vue";
import { ElMessage, ElMessageBox, ElLoading } from "element-plus";
import { Document } from "@element-plus/icons-vue";
import { calculateNodeCoordinates, assignNodeIndices } from "@/utils/nodePositionHandler";
import EdgeWithButton from "./components/EdgeWithButton.vue";
import axios from "axios";
const baseUrl = import.meta.env.VITE_BASE_URL;
const route = useRoute();
/**当前分析的产品id */
const productId = ref("-1");
/**后端接收的产品结构树 */
const tree = ref<Partial<ProductTree.ResProduct_Tree>>();
// 用户所属企业可以添加的所有材料列表
const materialList = ref();
// type TreeType = typeof tree;
/**产品名称 */
const rootname = ref("");
const nodes = ref<Node[]>([]);
const edges = ref<Edge[]>([]);
// 动态添加新节点的函数
const isModalAddOpen = ref(false); // 添加框
const isModalDelOpen = ref(false); // 删除框显示
const selectedNodeType = ref(""); // 存储选择的节点类型

const InputWindowRef = ref<InstanceType<typeof Structure_Input> | null>(null);
const InformationWindowRef = ref<InstanceType<typeof Structure_Information> | null>(null);
const tabStore = useTabsStore();
/**节点和其坐标 */
const nodeCoordinates = ref<ProductTree.NodeLocation[]>([]);
/**节点序号与materialId的映射关系 */
const indexToNodeMap = ref<Map<number, number>>(new Map());
/**以节点序号的边描述 */
const indexedEdges = ref<{ source: number; target: number }[]>([]);
const selectedMaterialId = ref<number | null>(null);
/**侧边栏视图控制 */
const sidebarView = ref("add"); // 'add' 或 'current'

/**返回主界面 */
const back = () => {
  // 判断该标签页是否固定
  if (route.meta.isAffix) return;
  tabStore.removeTabs(route.fullPath);
};

onMounted(async () => {
  productId.value = route.params.id as string;
  await initFormData();
  await initMaterialData();
  initializeNodesAndEdges();
});

/**获取产品结构树信息 */
const initFormData = async () => {
  try {
    const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
    const userId = Number(userInfo.id) || 0; // 如果没有 id，则使用默认值 0
    //数据库
    const response = await axios.post(baseUrl + "/api/carbon/getTreeByProductId?productId=" + productId.value, {
      userId: userId
    });
    if (response.data && response.data.code === 200) {
      tree.value = response.data.data;
      console.log("产品结构树:", tree.value);
      rootname.value = tree.value?.productName || "产品结构树";
    } else {
      // 处理后端返回的错误信息
      const errorMsg = response.data.msg || "获取产品结构树失败";
      console.error("获取产品结构树错误:", errorMsg);
      ElMessage.error(errorMsg);
    }
  } catch (error) {
    console.error("获取产品结构树出错:", error);
    ElMessage.error("获取产品结构树失败，可能是因为参数错误或网络问题");
  }
};

/**获取所有可添加材料信息 */
const initMaterialData = async () => {
  try {
    const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
    const userId = Number(userInfo.id) || 0; // 如果没有 id，则使用默认值 0
    //数据库
    const response = await axios.post(baseUrl + "/api/carbon/getAllMaterialsWithoutFilter", {
      userId: userId
    });
    if (response.data && response.data.code === 200) {
      materialList.value = response.data.data;
    } else {
      // 处理后端返回的错误信息
      const errorMsg = response.data.msg || "获取材料信息失败";
      console.error("获取材料信息错误:", errorMsg);
      ElMessage.error(errorMsg);
    }
  } catch (error) {
    console.error("获取材料信息出错:", error);
    ElMessage.error("获取材料信息失败，可能是因为参数错误或网络问题");
  }
};

/**初始化节点和连线 */
const initializeNodesAndEdges = () => {
  // 注册树中提到的所有材料
  if (!tree.value || !tree.value.node || !tree.value.edge) {
    ElMessage({
      type: "warning",
      message: "产品树为空!"
    });
    return;
  }
  // 处理特殊情况：只有根节点，没有边
  if (tree.value.edge.length === 0) {
    // 创建单独的根节点
    nodes.value = [
      {
        id: "1",
        position: { x: 0, y: 0 },
        data: {
          selected: false,
          materialID: -1,
          label: rootname.value,
          rootid: productId.value
        },
        style: {
          background: "#fff"
        },
        type: "productNode_outBuy"
      }
    ];
    edges.value = []; // 边为空数组
    return; // 提前返回，不执行后续逻辑
  }
  const result = assignNodeIndices(tree.value.edge);
  indexToNodeMap.value = result.indexToNodeMap;
  indexedEdges.value = result.indexedEdges;
  nodeCoordinates.value = calculateNodeCoordinates(indexedEdges.value, 1, 300, 80);
  // 添加节点
  const newNodes: (Node & { selected?: boolean })[] =
    nodeCoordinates.value.map(unitnode => {
      const materialID = indexToNodeMap.value.get(unitnode.index);
      // 没找到，则产品节点
      if (materialID === -1) {
        return {
          id: String(unitnode.index),
          position: { x: unitnode.x, y: unitnode.y }, // 节点垂直排列
          data: {
            selected: false,
            materialID: -1,
            label: rootname.value,
            rootid: productId.value
          },
          style: {
            background: "#fff"
          },
          type: "productNode_outBuy"
        };
      } else {
        const material = findMaterialInfoById(materialID)!;
        let nodeType = "";
        if (material.sourceType === 1) nodeType = "productNode_homeMade";
        else nodeType = "productNode_outBuy1";
        return {
          id: String(unitnode.index),
          position: { x: unitnode.x, y: unitnode.y }, // 节点垂直排列
          data: {
            selected: false,
            materialID: materialID,
            label: material.materialName
          },
          style: {
            background: "#fff"
          },
          type: nodeType
        };
      }
    }) || [];
  nodes.value = newNodes; // 更新节点
  // 添加边
  const newEdges: Edge[] =
    indexedEdges.value.map((unitnode, index) => {
      return {
        id: (index + 1).toString(),
        source: unitnode.source.toString(),
        target: unitnode.target.toString(),
        type: "button",
        markerEnd: {
          type: MarkerType.ArrowClosed,
          color: "#999999"
        }
      };
    }) || [];
  // 更新 edges.value
  edges.value = newEdges;
};

/**
 * 返回材料在tree.value.node中的信息
 * @param id 材料id
 */
function findMaterialInfoById(id: number | undefined) {
  if (id === undefined) return undefined;
  if (!tree.value || !tree.value.node) return undefined;
  return tree.value.node.find(material => material.id === id);
}

/**展示在左边的信息 */
const sidebarMaterials = computed(() => {
  if (materialList.value === undefined) return [];
  // 过滤出不在当前树中的材料，可以根据需要修改这个逻辑
  return materialList.value;
});

// 处理材料点击
const handleMaterialClick = (material: any) => {
  console.log("Material clicked:", material);
  // 可以添加点击处理逻辑
};

// 快速添加材料
const addMaterial = (material: any) => {
  addNode(material.id);
};

// 获取当前已添加的材料
const currentMaterials = computed(() => {
  if (!tree.value?.node) return [];

  // 过滤出当前树中包含的材料
  return tree.value.node.filter(node => node.id !== -1);
});

/**
 * 高亮显示与所选材料相关的节点
 * @param material 被点击的材料
 */
const highlightMaterialNodes = (material: any) => {
  // 更新选中的材料ID
  selectedMaterialId.value = material.id;
  // 首先重置所有节点的高亮状态
  nodes.value.forEach(node => {
    if (node.data) {
      node.style = { ...node.style, background: "#fff" };
    }
  });

  // 找到所有匹配该材料ID的节点并高亮显示
  nodes.value.forEach(node => {
    if (node.data && node.data.materialID === material.id) {
      node.style = { ...node.style, background: "#e0f2f1" };
    }
  });

  // 更新节点数组以触发重新渲染
  nodes.value = [...nodes.value];
};

/**
 * 清除选中状态
 */
const clearSelection = () => {
  selectedMaterialId.value = null;
  nodes.value.forEach(node => {
    if (node.data) {
      node.style = { ...node.style, background: "#fff" };
    }
  });
  nodes.value = [...nodes.value];
};

/**
 * 查询是否有环路
 * @param id1 起点
 * @param id2 终点
 */
function hasCycle(id1: string, id2: string): boolean {
  const graph = new Map<string, string[]>();

  // 构建图的邻接表表示
  edges.value.forEach(edge => {
    if (!graph.has(edge.source)) {
      graph.set(edge.source, []);
    }
    graph.get(edge.source)!.push(edge.target);
  });

  // 添加新的边
  if (!graph.has(id1)) {
    graph.set(id1, []);
  }
  graph.get(id1)!.push(id2);

  const visited = new Set<string>();
  const stack = new Set<string>();

  function dfs(node: string): boolean {
    if (stack.has(node)) return true; // 如果节点在当前路径中，说明存在回路
    if (visited.has(node)) return false; // 如果节点已经访问过，且不在当前路径中，说明没有回路

    visited.add(node);
    stack.add(node);

    const neighbors = graph.get(node) || [];
    for (const neighbor of neighbors) {
      if (dfs(neighbor)) return true;
    }

    stack.delete(node); // 回溯时移除节点
    return false;
  }

  return dfs(id1);
}

/**
 * 边连接前触发函数
 * @param edge 添加的边
 */
function onConnect1(edge: any) {
  console.log("connected start:", edge);
}
/**
 * 边连接后触发函数
 * @param edge 添加的边
 */
function onConnect2(edge: any) {
  if (hasCycle(edge.source, edge.target)) {
    ElMessage({
      type: "warning",
      message: "不能添加环路!"
    });
    return;
  }
  const newEdge: Edge = {
    id: `e${edge.source}-${edge.target}`,
    source: edge.source,
    target: edge.target,
    type: "button",
    markerEnd: {
      type: MarkerType.ArrowClosed,
      color: "#999999"
    }
  };
  edges.value.push(newEdge);
  console.log(edge.source, edge.target);
}

/**
 * 节点点击事件
 * @param node 点击节点内容
 */
function onNodeClick(node: any) {
  console.log(node);
}

/**
 * 通过层序遍历所有节点，返回materialID数组
 * @param tree 树
 */
// function traverseNodes() {
//   const result = []; // 存储遍历结果
//   console.log(nodes.value);
//   if (nodes.value) {
//     for (const node of nodes.value) {
//       result.push(node.data.materialID);
//     }
//   }
//   return result; // 返回层次遍历结果
// }

const closeAddModal = () => {
  isModalAddOpen.value = false;
  selectedNodeType.value = ""; // 清空选择
};
const closeDelModal = () => {
  isModalDelOpen.value = false;
  selectedNodeType.value = ""; // 清空选择
};

/**
 * 返回材料在MateriaList中的下标
 * @param id 材料id
 */
function findMaterialIndexById(id: number | undefined) {
  return materialList.value.findIndex((material: any) => material.id === id);
}

/**
 * 添加节点
 * @param id 材料id
 */
const addNode = (id: number) => {
  // 找到 ID 最大的节点
  const maxNode = nodes.value.reduce((max, node) => (parseInt(node.id) > parseInt(max.id) ? node : max), nodes.value[0]);
  // 获取当前最大 ID 并生成新的 ID
  const newNodeId = (parseInt(maxNode.id) + 1).toString();
  // 计算新节点的位置（在当前最大节点的正下方）
  const newNodePosition = {
    x: maxNode.position.x + 100,
    y: maxNode.position.y
  };
  //获取名称
  const materialIndex = findMaterialIndexById(id);
  const material = materialList.value[materialIndex];
  if (!material) {
    ElMessage({
      type: "warning",
      message: "物料不存在!"
    });
    return;
  }
  // 创建新节点
  let nodeType = "";
  if (material.sourceType === 1) nodeType = "productNode_homeMade";
  else nodeType = "productNode_outBuy1";
  const newNode: Node = {
    id: newNodeId,
    label: selectedNodeType.value, // 使用选择的节点类型作为名称
    position: newNodePosition,
    style: { background: "#fff" },
    data: { selected: false, materialID: id, label: material.materialName },
    type: nodeType
  };
  nodes.value.push(newNode);
  Node_addCheck(getMaterialNode());
  closeAddModal();
};

const deleteNode = () => {
  // 找到要删除的节点
  const selectedNode = nodes.value.find(node => node.selected);
  if (!selectedNode) {
    ElMessage({
      type: "warning",
      message: "请选择一个物料!"
    });
    return;
  }
  let id = selectedNode.id;
  if (selectedNode.id == "1") {
    ElMessage({
      type: "warning",
      message: "产品节点不能删除!"
    });
    return;
  }
  const nodeIndex = nodes.value.findIndex(node => node.id === id);
  // 删除节点
  nodes.value.splice(nodeIndex, 1);
  // 删除与该节点相关的边
  edges.value = edges.value.filter(edge => edge.source !== id && edge.target !== id);
  Edge_deleteCheck(getMaterialST());
  Node_deleteCheck(getMaterialNode());
  // 关闭模态框
  closeDelModal();
};

/**
 * 打开输入内容函数，将已加入的内容输入
 */
// const openInputWindow = () => {
//   let leaves = traverseNodes().filter(leaf => leaf !== "-1") as string[];
//   if (InputWindowRef.value) {
//     InputWindowRef.value.openDialog(leaves);
//   }
// };
const openInformWindow = (nodeid: string, source: number) => {
  if (InformationWindowRef.value) {
    InformationWindowRef.value.openDialog(nodeid, source);
  }
};

/**
 * 按下toolBar按钮的处理函数
 */
const handleToolbarAction = (action: string, materialId: string, source: number) => {
  if (action === "详情") openInformWindow(materialId, source);
  else {
    ElMessageBox.confirm("是否删除所选项?", "温馨提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
      draggable: true
    }).then(() => {
      deleteNode(); // 调用删除逻辑
      ElMessage({
        type: "success",
        message: "删除成功!"
      });
    });
  }
};
/**
 * 处理structure_Input传过来的值
 */
const handleNodeAdd = (selectedItems1: any[], selectedItems2: any[]) => {
  if (selectedItems1.length === 1) addNode(selectedItems1[0].id);
  if (selectedItems2.length === 1) addNode(selectedItems2[0].id);
  // 在这里处理传递过来的值
};

const saveModel = async () => {
  try {
    Edge_addCheck(getMaterialST());
    Edge_deleteCheck(getMaterialST());
    Node_addCheck(getMaterialNode());
    Node_deleteCheck(getMaterialNode());
    // 获取用户信息
    const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
    const userId = Number(userInfo.id) || 0;
    const requestData = {
      userId: userId,
      edge:
        tree.value?.edge?.map(edge => ({
          source: edge.source,
          target: edge.target
        })) || [],
      node: tree.value?.node?.filter(node => node.id !== -1).map(node => node.id) || []
    };
    console.log("请求数据:", requestData);
    const loading = ElLoading.service({
      lock: true,
      text: "正在保存产品树结构...",
      background: "rgba(0, 0, 0, 0.7)"
    });
    // 发送请求
    const response = await axios.post(`${baseUrl}/api/carbon/updateTree?productId=${productId.value}`, requestData);
    // 关闭加载提示
    loading.close();
    if (response.data && response.data.code === 200) {
      ElMessage({
        type: "success",
        message: "保存成功!"
      });
    } else {
      ElMessage({
        type: "error",
        message: response.data?.msg || "保存失败!"
      });
    }
  } catch (error) {
    console.error("保存产品树结构出错:", error);
    ElMessage({
      type: "error",
      message: "保存失败，可能是因为网络问题或服务器错误"
    });
  }
};

/**
 * 从edges获取当前所有边的起点和终点（转化为材料id）
 * @returns 边数组{source: number, target: number}[]
 */
const getMaterialST = () => {
  let result = [] as any;
  edges.value.forEach(edge => {
    let source_old = edge.source;
    let source_new = nodes.value.find(node => node.id === source_old)?.data.materialID;
    let target_old = edge.target;
    let target_new = nodes.value.find(node => node.id === target_old)?.data.materialID;
    result.push({ source: source_new, target: target_new });
  });
  return result;
};
/**
 * 检查tree.edge是否与edges同步
 * 不同步则筛选出edges中有而tree中没有的组合，将其加入tree中
 * @param newEdges 来自getMaterialST的结果
 */
const Edge_addCheck = (newEdges: { source: number; target: number }[]) => {
  // 检查tree中没有的内容
  newEdges.forEach(edge => {
    if (!tree.value?.edge?.some(e => e.source === edge.source && e.target === edge.target)) {
      addTree_Edge(edge.source, edge.target);
    }
  });
};
/**向tree.edge中添加一条边
 * @param source 起点材料id
 * @param target 终点材料id
 */
function addTree_Edge(source: number, target: number) {
  if (tree.value && tree.value.edge) {
    tree.value.edge.push({ source: source, target: target });
  }
}
/**
 * 从tree中去掉边
 * @param newEdges 来自getMaterialST的结果
 */
const Edge_deleteCheck = (newEdges: { source: number; target: number }[]) => {
  // 遍历tree.value?.edge?并检测中是否有newEdges中没有的内容，如果有的话则去掉
  const edgesToRemove = tree.value?.edge?.filter(edge => {
    return !newEdges.some(newEdge => newEdge.source === edge.source && newEdge.target === edge.target);
  });

  edgesToRemove?.forEach(edge => {
    const index = tree.value?.edge?.indexOf(edge);
    if (index !== undefined && index !== -1) {
      tree.value?.edge?.splice(index, 1);
    }
  });
};

/**
 * 获取当前nodes中所有节点的材料id
 * @returns 材料id数组(number[])
 */
const getMaterialNode = () => {
  let result = [] as any;
  nodes.value.forEach(node => {
    let index = node.data.materialID;
    result.push(index);
  });
  return result;
};
/**
 * 检查tree.value.node是否与nodes同步
 * 不同步则筛选出nodes中有而tree中没有的材料id(number)，将其加入tree中
 * @param newNodes 来自getMaterialNode的结果(number[])
 */
const Node_addCheck = (newNodes: number[]) => {
  // 检查tree中没有的内容
  newNodes.forEach(node => {
    if (!tree.value?.node?.some(e => e.id === node)) {
      addTree_Node(node);
    }
  });
};
/**
 * 向tree.value.node中添加一个节点
 * @param materialIndex 材料id
 */
function addTree_Node(materialIndex: number) {
  const node = nodes.value.find(node => node.data.materialID === materialIndex);
  if (!node) return;
  if (tree.value && tree.value.node) {
    tree.value.node.push({
      id: materialIndex,
      materialName: node.data.label,
      sourceType: node.type === "productNode_outBuy1" ? 2 : 1
    });
  }
}
/**
 * 从productTree中去掉原先的点
 * @param newNodes 来自getMaterialNode的结果(number[])
 */
const Node_deleteCheck = (newNodes: number[]) => {
  // 遍历tree.value?.node?并检测中是否有newNodes中没有的内容，如果有的话则去掉
  const nodesToRemove = tree.value?.node?.filter(node => {
    return !newNodes.some(newNode => newNode === node.id);
  });

  nodesToRemove?.forEach(node => {
    const index = tree.value?.node?.indexOf(node);
    if (index !== undefined && index !== -1) {
      tree.value?.node?.splice(index, 1);
    }
  });
};
</script>

<style>
.content {
  display: flex;
  height: 100%;
  overflow: hidden;
}

.sidebar {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 15px;
  width: 18%;
  min-width: 180px;
  max-width: 300px;
  padding: 20px;
  overflow-y: auto;
  background-color: #f4f6f8;
  border-right: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgb(0 0 0 / 10%);
}

.sidebar .memo-item {
  padding: 12px 16px;
  margin-bottom: 3px;
  cursor: pointer;
  background-color: #ffffff;
  border: 1px solid #dddddd;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
  transition: background-color 0.3s;
}

.sidebar .memo-item:hover {
  background-color: #e6f5f3;
}

.sidebar .memo-name {
  margin-bottom: 10px;
  font-size: 16px;
  font-weight: bold;
}

.sidebar .memo-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.sidebar .memo-status {
  flex: 1;
  padding-right: 10px;
  font-size: 14px;
}

.sidebar .memo-button {
  padding: 6px 12px;
  color: white;
  cursor: pointer;
  background-color: #009688;
  border: none;
  border-radius: 5px;
  transition: background-color 0.3s;
}

.sidebar .memo-button:hover {
  background-color: #007d6a;
}
/* 选中材料项的样式 */
.memo-item-selected {
  background-color: #b2dfdb !important; /* 改为更浅的绿色 */
  border-left: 4px solid #009688; /* 保持深绿色边框作为高亮指示 */
  transform: translateX(15px);
  /* 移除文字颜色修改，保持原来的黑色文字以提高可读性 */
}

/* 确保选中效果优先级高于悬停效果 */
.sidebar .memo-item-selected:hover {
  background-color: #b2dfdb !important; /* 鼠标悬停时保持相同的浅色 */
}

.sidebar-tabs {
  display: flex;
  margin-bottom: 15px;
  border-bottom: 1px solid #e0e0e0;
}

.sidebar-tab {
  flex: 1;
  padding: 8px 0;
  margin: 0;
  font-size: 14px;
  color: #555;
  cursor: pointer;
  background-color: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  transition: all 0.3s ease;
}

.sidebar-tab:hover {
  color: #009688;
  background-color: transparent;
}

.sidebar-tab.active {
  color: #009688;
  font-weight: bold;
  background-color: transparent;
  border-bottom: 2px solid #009688;
}

.sidebar-content {
  display: flex;
  flex-direction: column;
  gap: 15px;
  overflow-y: auto;
}

.flow-container {
  position: relative;
  flex: 1;
  overflow: hidden;
}
.header {
  display: flex;
  align-items: center;
  justify-content: space-between; /* 左右分布 */
  padding: 10px 20px;
  background-color: #ffffff;
  border-bottom: 1px solid #dddddd;
  border-radius: 8px;
}
.header-buttons {
  display: flex;
  gap: 10px; /* 按钮之间的间距 */
  margin-left: auto; /* 将按钮推到最右侧 */
}
.back-button {
  padding: 8px 12px;
  margin-right: 20px;
  font-size: 14px;
  color: white;
  cursor: pointer;
  background-color: #009688;
  border: none;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}
.back-button:hover {
  background-color: #007d6a;
}
.title {
  font-size: 18px;
  font-weight: bold;
  color: #333333;
}
.modal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: rgb(0 0 0 / 50%);
}
.modal-content {
  z-index: 9999;
  width: 400px;
  max-width: 90%;
  padding: 20px;
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgb(0 0 0 / 10%);
}
select {
  z-index: 10000;
  width: 100%;
  padding: 8px;
  margin: 10px 0;
  border: 1px solid #cccccc;
  border-radius: 4px;
}
button {
  padding: 8px 12px;
  margin: 5px;
  color: white;
  cursor: pointer;
  background-color: #009688;
  border: none;
  border-radius: 4px;
}
button:hover {
  background-color: #009688;
}
.customNode {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100px;
  height: 50px;
  cursor: pointer;
  border: 1px solid #cccccc;
  border-radius: 4px;
}
.customNode.selected {
  color: white;
  background-color: #4caf50;
}
.vue-flow__node-toolbar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  padding: 8px;
  background-color: #2d3748;
  border-radius: 8px;
  box-shadow: 0 0 10px rgb(0 0 0 / 50%);
}
.vue-flow__node-toolbar button {
  padding: 0.5rem;
  color: white;
  cursor: pointer;
  background: #4a5568;
  border: none;
  border-radius: 8px;
}
.vue-flow__node-toolbar button.selected {
  background: #009688;
}
.vue-flow__node-toolbar button:hover {
  background: #009688;
}
.vue-flow__node-productNode_homeMade {
  padding: 20px 24px;
  color: #009688;
  background: #e6f5f3;
  border: 1px solid #009688;
  border-radius: 4px;
  box-shadow: 0 0 10px rgb(0 0 0 / 20%);
}
.vue-flow__node-productNode_homeMade.selected {
  border: 1px solid red;
}
.vue-flow__node-productNode_outBuy {
  padding: 20px 24px;
  color: #009688;
  background: #e6f5f3;
  border: 1px solid #009688;
  border-radius: 4px;
  box-shadow: 0 0 10px rgb(0 0 0 / 20%);
}
.vue-flow__node-productNode_outBuy.selected {
  border: 1px solid red;
}
.vue-flow__node-productNode_outBuy1 {
  padding: 20px 24px;
  color: #009688;
  background: #e6f5f3;
  border: 1px solid #009688;
  border-radius: 4px;
  box-shadow: 0 0 10px rgb(0 0 0 / 20%);
}
.vue-flow__node-productNode_outBuy1.selected {
  border: 1px solid red;
}
</style>
